use hello::ThreadPool;
use std::{
    fs,
    io::{prelude::*, BufRead, BufReader},
    net::{TcpListener, TcpStream},
    thread,
    time::Duration,
};

fn main() {
    let listener = TcpListener::bind("127.0.0.1:7878").expect("Failed to bind address");
    let pool = ThreadPool::new(4);

    println!("Listening on http://127.0.0.1:7878 ...");

    for stream in listener.incoming() {
        let stream = match stream {
            Ok(stream) => stream,
            Err(err) => {
                eprintln!("Failed to accept connection: {err:?}");
                continue;
            }
        };

        pool.execute(move || handle_connection(stream));
    }

    println!("Server shutting down.");
}

fn handle_connection(mut stream: TcpStream) {
    let mut buf_reader = BufReader::new(&mut stream);
    let mut request_line = String::new();

    if buf_reader.read_line(&mut request_line).is_err() {
        eprintln!("Failed to read request line");
        return;
    }

    let request_line = request_line.trim_end();

    let (status_line, filename) = match request_line {
        "GET / HTTP/1.1" | "GET / HTTP/1.0" => ("HTTP/1.1 200 OK", "hello.html"),
        "GET /sleep HTTP/1.1" | "GET /sleep HTTP/1.0" => {
            println!("Simulating slow request: /sleep");
            thread::sleep(Duration::from_secs(5));
            ("HTTP/1.1 200 OK", "hello.html")
        }
        _ => ("HTTP/1.1 404 NOT FOUND", "404.html"),
    };

    let contents = match fs::read_to_string(filename) {
        Ok(contents) => contents,
        Err(err) => {
            eprintln!("Failed to read file `{filename}`: {err:?}");
            let fallback = "<h1>500 Internal Server Error</h1>";
            format!("{fallback}")
        }
    };

    let response = format!(
        "{status_line}\r\nContent-Length: {}\r\nContent-Type: text/html; charset=utf-8\r\n\r\n{contents}",
        contents.len()
    );

    if let Err(err) = stream.write_all(response.as_bytes()) {
        eprintln!("Failed to write response: {err:?}");
    }

    if let Err(err) = stream.flush() {
        eprintln!("Failed to flush stream: {err:?}");
    }
}
